﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Net;
using System.IO;
using System.Xml;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using System.Web;
using Ecommerce.Pagamento.Redecard;
using Ecommerce.Pagamento.RedecardTeste;

namespace Ecommerce.Pagamento.CartaoCredito
{
    /// <summary>
    /// Efetua pagamento com a Redecard através do webservice
    /// </summary>
    public class PagamentoRedecard
    {
        /// <summary>
        /// URL do webservice da redecard
        /// </summary>
        #if DEBUG
                private const string REDECARD_WEBSERVICE_URL = "https://ecommerce.redecard.com.br/pos_virtual/wskomerci/cap_teste.asmx";
        #else
                private const string REDECARD_WEBSERVICE_URL = "https://ecommerce.redecard.com.br/pos_virtual/wskomerci/cap.asmx";
        #endif

        /// <summary>
        /// Url do link para gerar o cupom de confirmação de transação da redecard
        /// </summary>
        private const string REDECARD_CUPOM_URL = "https://ecommerce.redecard.com.br/pos_virtual/cupom.asp";

        /// <summary>
        /// Faz log das transações
        /// </summary>
        private LogWriter _logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();

        #region :: EfetuarPagamento ::
        /// <summary>
        /// Efetua o pagamento
        /// </summary>
        /// <param name="autorizacao">Autorização para fazer o pagamento</param>
        /// <returns>Retorna o cupom da transação</returns>
        public ERetornoPagamento EfetuarPagamento(EAutorizacao autorizacao, string identificadorCliente)
        {
            try
            {
                ERetornoPagamento retornoPagamento = null;
                if (this.ValidarAutorizacao(autorizacao))
                {
                    #if DEBUG
                         komerci_capture_teste clientCapture = new komerci_capture_teste();
                    #else
                          komerci_capture clientCapture = new komerci_capture();
                    #endif
                    
                    
                    clientCapture.Url = REDECARD_WEBSERVICE_URL;

                    // Realizo a autorização da transação
                    #if DEBUG
                        XmlNode nodeAutorizacao = clientCapture.GetAuthorizedTst(autorizacao.Total, autorizacao.Transacao, autorizacao.Parcelas, autorizacao.Filiacao,
                        autorizacao.NumeroPedidoEstabelecimento, autorizacao.NumeroCartao, autorizacao.CodigoSegurancaCartao, autorizacao.ValidadeMes,
                        autorizacao.ValidadeAno, autorizacao.Portador, "", "", "", "", "", autorizacao.PAX1, autorizacao.PAX2, autorizacao.PAX3, autorizacao.PAX4,
                        autorizacao.NUMDOC1, autorizacao.NUMDOC2, autorizacao.NUMDOC3, autorizacao.NUMDOC4, "N", null);
                    #else
                        XmlNode nodeAutorizacao = clientCapture.GetAuthorized(autorizacao.Total, autorizacao.Transacao, autorizacao.Parcelas, autorizacao.Filiacao,
                        autorizacao.NumeroPedidoEstabelecimento, autorizacao.NumeroCartao, autorizacao.CodigoSegurancaCartao, autorizacao.ValidadeMes,
                        autorizacao.ValidadeAno, autorizacao.Portador, null, null, null, null, null, autorizacao.PAX1, autorizacao.PAX2, autorizacao.PAX3, autorizacao.PAX4,
                        autorizacao.NUMDOC1, autorizacao.NUMDOC2, autorizacao.NUMDOC3, autorizacao.NUMDOC4, null, null);
                    #endif

                    string xml = nodeAutorizacao.InnerXml.Insert(0, "<root>");
                    xml = xml + "</root>";

                    ERetornoAutorizacao retornoAutorizacao = this.ParaRetornoAutorizacao(xml);

                    if (!retornoAutorizacao.CodigoRetorno.Equals("0"))
                        throw new PagamentoRedecardException(int.Parse(retornoAutorizacao.CodigoRetorno), PagamentoRedecardTipoTransacaoErro.Autorizacao, retornoAutorizacao.DescricaoCodigoRetorno);

                    // Realizo a confirmação da transação
                    #if DEBUG
                        XmlNode nodeConfirmacao = clientCapture.ConfirmTxnTst(retornoAutorizacao.DataTransacao, retornoAutorizacao.NumeroSequencialUnico, retornoAutorizacao.NumeroComprovanteVenda,
                        retornoAutorizacao.NumeroAutorizacao, autorizacao.Parcelas, autorizacao.Transacao, autorizacao.Total, autorizacao.Filiacao,
                        "", retornoAutorizacao.NumeroPedido, null, null, null, null, null, null, null, null, null);
                    #else
                        XmlNode nodeConfirmacao = clientCapture.ConfirmTxn(retornoAutorizacao.DataTransacao, retornoAutorizacao.NumeroSequencialUnico, retornoAutorizacao.NumeroComprovanteVenda,
                        retornoAutorizacao.NumeroAutorizacao, autorizacao.Parcelas, autorizacao.Transacao, autorizacao.Total, autorizacao.Filiacao,
                        null, retornoAutorizacao.NumeroPedido, null, null, null, null, null, null, null, null, null);
                    #endif


                    // Verifico o retorno do xml
                    xml = nodeConfirmacao.InnerXml.Insert(0, "<root>");
                    xml = xml + "</root>";

                    XDocument xDocument = XDocument.Parse(xml);
                    var q = (
                        from
                            n
                        in
                            xDocument.Elements()
                        select new { Codigo = n.Element("CODRET").Value, Descricao = n.Element("CODRET").Value }).First();

                    if (!q.Codigo.Equals("0") && !q.Codigo.Equals("1"))
                        throw new PagamentoRedecardException(int.Parse(q.Codigo), PagamentoRedecardTipoTransacaoErro.ConfirmarTransacao, q.Descricao);

                    retornoPagamento = new ERetornoPagamento();

                    retornoPagamento.DataTransacao = retornoAutorizacao.DataTransacao;
                    retornoPagamento.Filiacao = autorizacao.Filiacao;
                    retornoPagamento.NumeroAutorizacao = retornoAutorizacao.NumeroAutorizacao;
                    retornoPagamento.NumeroComprovanteVendas = retornoAutorizacao.NumeroComprovanteVenda;
                    retornoPagamento.NumeroPedidoEstabelecimento = autorizacao.NumeroPedidoEstabelecimento;
                }
                return retornoPagamento;
            }
            catch (PagamentoRedecardException pre)
            {
                LogEntry log = new LogEntry();
                log.EventId = pre.CodigoErro;
                log.Title = "Erro ao realizar pagamento Redecard / Cliente: " + identificadorCliente;
                log.Message = pre.Message + Environment.NewLine + pre.StackTrace;

                if (pre.InnerException != null)
                    log.Message += Environment.NewLine + pre.InnerException.Message;

                log.Severity = System.Diagnostics.TraceEventType.Error;
                log.TimeStamp = DateTime.Now;

                _logWriter.Write(log);

                throw pre;
            }
            catch (Exception ex)
            {
                LogEntry log = new LogEntry();
                log.EventId = 99;
                log.Title = "Erro ao realizar pagamento Redecard / Cliente: " + identificadorCliente;
                log.Message = ex.Message + Environment.NewLine + ex.StackTrace;
                log.Severity = System.Diagnostics.TraceEventType.Error;
                log.TimeStamp = DateTime.Now;

                _logWriter.Write(log);

                throw ex;
            }
        }
        #endregion

        #region :: ValidarAutorizacao ::
        /// <summary>
        /// Valida os dados da autorização para realizar o pagamento
        /// </summary>
        /// <param name="autorizacao"></param>
        /// <returns></returns>
        private bool ValidarAutorizacao(EAutorizacao autorizacao)
        {
            if (autorizacao == null)
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("EAutorizacao", "Objeto autorização não pode ser null"));

            if (string.IsNullOrEmpty(autorizacao.Total))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("Total", "Propriedade não informada"));

            if (autorizacao.Total.Length > 14)
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("Total", "Propriedade deve ter no maximo 14 caracteres"));

            if (string.IsNullOrEmpty(autorizacao.Transacao))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("Transacao", "Propriedade não informada"));

            if (!autorizacao.Transacao.Length.Equals(2))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("Transacao", "Propriedade deve ter 2 caracteres"));

            if (string.IsNullOrEmpty(autorizacao.Parcelas))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("Parcelas", "Propriedade não informada"));

            if (!autorizacao.Parcelas.Length.Equals(2))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("Parcelas", "Propriedade deve ter 2 caracteres"));

            if (string.IsNullOrEmpty(autorizacao.Filiacao))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("Filiacao", "Propriedade não informada"));

            if (string.IsNullOrEmpty(autorizacao.NumeroPedidoEstabelecimento))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("NumeroPedidoEstabelecimento", "Propriedade não informada"));

            if (string.IsNullOrEmpty(autorizacao.NumeroCartao))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("NumeroCartao", "Propriedade não informada"));

            if (autorizacao.NumeroCartao.Length > 16)
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("NumeroCartao", "Propriedade deve ter no máximo 16 caracteres"));
            
            if (string.IsNullOrEmpty(autorizacao.CodigoSegurancaCartao))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("CodigoSegurancaCartao", "Propriedade não informada"));

            if (!autorizacao.CodigoSegurancaCartao.Length.Equals(3))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("CodigoSegurancaCartao", "Propriedade deve ter 3 caracteres"));

            if (string.IsNullOrEmpty(autorizacao.ValidadeMes))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("ValidadeMes", "Propriedade não informada"));

            if (!autorizacao.ValidadeMes.Length.Equals(2))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("ValidadeMes", "Propriedade deve ter 2 caracteres"));

            if (string.IsNullOrEmpty(autorizacao.ValidadeAno))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("ValidadeAno", "Propriedade não informada"));

            if (!autorizacao.ValidadeAno.Length.Equals(2))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("ValidadeAno", "Propriedade deve ter 2 caracteres"));

            if (string.IsNullOrEmpty(autorizacao.Portador))
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentNullException("Portador", "Propriedade não informada"));

            if (autorizacao.Portador.Length > 50)
                throw new PagamentoRedecardException(99, PagamentoRedecardTipoTransacaoErro.ValidacaoObjetoAutorizacao, "Parametro Incorreto. Veja innerException", new ArgumentOutOfRangeException("Portador", "Propriedade deve ter no maximo 50 caracteres"));

            return true;
        }
        #endregion

        #region :: ParaRetornoAutorizacao ::
        /// <summary>
        /// Converte o XML de retorno da autorizacao para a classe ERetornoAutorizacao
        /// </summary>
        /// <param name="xml">
        /// xml no formato de retorno da autorização
        /// </param>
        /// <returns></returns>
        private ERetornoAutorizacao ParaRetornoAutorizacao(string xml)
        {
            XDocument xDocument = XDocument.Parse(xml);

            ERetornoAutorizacao retorno = (
                from
                    n
                in
                    xDocument.Elements()
                select new ERetornoAutorizacao
                {
                    CodigoRetorno = n.Element("CODRET") != null ? n.Element("CODRET").Value : string.Empty,
                    DescricaoCodigoRetorno = n.Element("MSGRET") != null ? HttpUtility.UrlDecode(n.Element("MSGRET").Value, Encoding.Default) : string.Empty,
                    NumeroPedido = n.Element("NUMPEDIDO") != null ? n.Element("NUMPEDIDO").Value : string.Empty,
                    DataTransacao = n.Element("DATA") != null ? n.Element("DATA").Value : string.Empty,
                    NumeroAutorizacao = n.Element("NUMAUTOR") != null ? n.Element("NUMAUTOR").Value : string.Empty,
                    NumeroComprovanteVenda = n.Element("NUMCV")!= null ? n.Element("NUMCV").Value : string.Empty,
                    NumeroAutenticacao = n.Element("NUMAUTENT") != null ? n.Element("NUMAUTENT").Value : string.Empty,
                    NumeroSequencialUnico = n.Element("NUMSQN") != null ? n.Element("NUMSQN").Value : string.Empty,
                    Origem = n.Element("ORIGEM_BIN") != null ? n.Element("ORIGEM_BIN").Value : string.Empty,
                    CodigoRetornoConfirmacaoAutomatica = n.Element("CONFCODRET") != null ? n.Element("CONFCODRET").Value : string.Empty,
                    DescricaoCodigoRetornoConfirmacaoAutomatica = n.Element("CONFMSGRET") != null ? n.Element("CONFMSGRET").Value : string.Empty,
                    IATA = n.Element("IATA") != null ? n.Element("IATA").Value : string.Empty,
                    Distribuidor = n.Element("DISTRIBUIDOR") != null ? n.Element("DISTRIBUIDOR").Value : string.Empty
                }
                ).First();

            return retorno;
        }
        #endregion

        #region :: GerarCupom ::
        public string GerarCupom(string dataTransacao, string numeroAutorizacao, string numeroComprovanteVendas, string numeroFiliacaoEstabelecimento)
        {
            string parametrosPost = "?DATA=" + dataTransacao + "&TRANSACAO=201&NUMAUTOR=" + numeroAutorizacao + "&NUMCV=" + numeroComprovanteVendas + "&FILIACAO=" + numeroFiliacaoEstabelecimento;
            byte[] byteArray = Encoding.UTF8.GetBytes(parametrosPost);

            Uri url = new Uri(REDECARD_CUPOM_URL);

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Credentials = CredentialCache.DefaultCredentials;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = byteArray.Length;

            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            WebResponse response = request.GetResponse();
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string htmlCupom = reader.ReadToEnd();

            reader.Close();
            dataStream.Close();
            response.Close();

            reader.Dispose();
            dataStream.Dispose();

            return htmlCupom;
        }
        #endregion
    }
}